<script type="text/html" data-template-name="link in">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
        <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
    </div>
    <div style="position:relative; height: 30px; text-align: right;"><div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div></div>
    <div class="form-row node-input-link-row"></div>
</script>
<script type="text/html" data-template-name="link out">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
        <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
    </div>
    <div class="form-row">
        <label for="node-input-mode"><span data-i18n="link.outMode"></span></label>
        <select id="node-input-mode" style="width: 70%">
            <option value="link" selected data-i18n="link.sendToAll"></option>
            <option value="return" data-i18n="link.returnToCaller"></option>
        </select>
    </div>
    <div class="node-input-link-rows" style="position:relative; height: 30px; text-align: right;"><div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div></div>
    <div class="form-row node-input-link-row node-input-link-rows"></div>
</script>

<script type="text/html" data-template-name="link call">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
        <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
    </div>
    <div class="form-row">
        <label for="node-input-timeout"><i class="fa fa-clock-o"></i> <span data-i18n="exec.label.timeout"></span></label>
        <input type="text" id="node-input-timeout" placeholder="30" style="width: 70px; margin-right: 5px;"><span data-i18n="inject.seconds"></span>
    </div>
    <div class="form-row">
        <label for="node-input-linkType" data-i18n="link.linkCallType"></label>
        <select id="node-input-linkType" style="width: 70%">
            <option value="static" data-i18n="link.staticLinkCall"></option>
            <option value="dynamic" data-i18n="link.dynamicLinkCall"></option>
        </select>
    </div>
    <div class="link-call-target-tree" style="position:relative; height: 30px; text-align: right;">
        <div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div>
    </div>
    <div class="form-row node-input-link-row link-call-target-tree"></div>
</script>

<script type="text/javascript">
(function() {

    let treeList;

    function onEditPrepare(node,targetType) {
        if (!node.links) {
            node.links = [];
        }
        node.oldLinks = [];

        const activeSubflow = RED.nodes.subflow(node.z);

        treeList = $("<div>")
            .css({width: "100%", height: "100%"})
            .appendTo(".node-input-link-row")
            .treeList({autoSelect:false})
            .on('treelistitemmouseover',function(e,item) {
                if (item.node) {
                    item.node.highlighted = true;
                    item.node.dirty = true;
                    RED.view.redraw();
                }
            })
            .on('treelistitemmouseout',function(e,item) {
                if (item.node) {
                    item.node.highlighted = false;
                    item.node.dirty = true;
                    RED.view.redraw();
                }
            });
        const candidateNodes = RED.nodes.filterNodes({type:targetType});
        let candidateNodesCount = 0;

        const search = $("#node-input-link-target-filter").searchBox({
            style: "compact",
            delay: 300,
            change: function() {
                var val = $(this).val().trim().toLowerCase();
                if (val === "") {
                    treeList.treeList("filter", null);
                    search.searchBox("count","");
                } else {
                    const count = treeList.treeList("filter", function(item) {
                        return item.label.toLowerCase().indexOf(val) > -1 || (item.node && item.node.type.toLowerCase().indexOf(val) > -1)
                    });
                    search.searchBox("count",count+" / "+candidateNodesCount);
                }
            }
        });

        const flows = [];
        const flowMap = {};

        if (activeSubflow) {
            flowMap[activeSubflow.id] = {
                id: activeSubflow.id,
                class: 'red-ui-palette-header',
                label: "Subflow : " + (activeSubflow.name || activeSubflow.id),
                expanded: true,
                children: []
            };
            flows.push(flowMap[activeSubflow.id])
        }
        if (!activeSubflow || node.type === "link call") {
            // Only "Link Call" can look outside of its own subflow
            // Link In and Link Out nodes outside of a subflow should be ignored
            RED.nodes.eachWorkspace(function (ws) {
                flowMap[ws.id] = {
                    id: ws.id,
                    class: 'red-ui-palette-header',
                    label: (ws.label || ws.id) + (node.z === ws.id ? " *" : ""),
                    expanded: true,
                    children: []
                }
                flows.push(flowMap[ws.id])
            })
        }

        candidateNodes.forEach(function (n) {
            if (flowMap[n.z]) {
                if (targetType === "link out" && n.mode === 'return') {
                    // Link In nodes looking for Link Out nodes should not
                    // include return-mode nodes.
                    return;
                }
                const isChecked = (node.links.indexOf(n.id) !== -1) || (n.links || []).indexOf(node.id) !== -1;
                if (isChecked) {
                    node.oldLinks.push(n.id);
                }
                flowMap[n.z].children.push({
                    id: n.id,
                    node: n,
                    label: n.name || n.id,
                    selected: isChecked,
                    checkbox: node.type !== "link call",
                    radio: node.type === "link call"
                })
                candidateNodesCount++;
            }
        });
        const flowsFiltered = flows.filter(function(f) { return f.children.length > 0 })
        treeList.treeList('data',flowsFiltered);
        setTimeout(function() {
            treeList.treeList('show',node.z);
        },100);
    }

    function resizeNodeList() {
        var rows = $("#dialog-form>div:not(.node-input-link-row)");
        var height = $("#dialog-form").height();
        for (var i=0;i<rows.length;i++) {
            height -= $(rows[i]).outerHeight(true);
        }
        var editorRow = $("#dialog-form>div.node-input-link-row");
        height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
        $(".node-input-link-row").css("height",height+"px");
    }

    function onEditSave(node) {
        var flows = treeList.treeList('data');
        node.links = [];
        if (node.type !== "link out" || $("#node-input-mode").val() === 'link') {
            flows.forEach(function(f) {
                f.children.forEach(function(n) {
                    if (n.selected) {
                        node.links.push(n.id);
                    }
                })
            })
        }
        node.oldLinks.sort();
        node.links.sort();

        if (node.type === "link call") {
            return
        }

        var nodeMap = {};
        var length = Math.max(node.oldLinks.length,node.links.length);
        for (var i=0;i<length;i++) {
            if (i<node.oldLinks.length) {
                nodeMap[node.oldLinks[i]] = nodeMap[node.oldLinks[i]]||{};
                nodeMap[node.oldLinks[i]].old = true;
            }
            if (i<node.links.length) {
                nodeMap[node.links[i]] = nodeMap[node.links[i]]||{};
                nodeMap[node.links[i]].new = true;
            }
        }

        let editHistories = []
        let n;
        for (let id in nodeMap) {
            if (nodeMap.hasOwnProperty(id)) {
                n = RED.nodes.node(id);
                if (n) {
                    editHistories.push({
                        t:'edit',
                        node: n,
                        changes: {
                            links: [...n.links]
                        },
                        changed: n.changed
                    })
                    if (nodeMap[id].old && !nodeMap[id].new) {
                        // Removed id
                        i = n.links.indexOf(node.id);
                        if (i > -1) {
                            n.links.splice(i,1);
                            n.changed = true
                            n.dirty = true
                        }
                    } else if (!nodeMap[id].old && nodeMap[id].new) {
                        // Added id
                        i = n.links.indexOf(id);
                        if (i === -1) {
                            n.links.push(node.id);
                            n.changed = true
                            n.dirty = true
                        }
                    }
                }
            }
        }
        if (editHistories.length > 0) {
            return {
                history: editHistories
            }
        }
    }

    function onAdd() {
        if (this.name === '_DEFAULT_') {
            this.name = ''
            RED.actions.invoke("core:generate-node-names", this, {generateHistory: false})
        }
        for (var i=0;i<this.links.length;i++) {
            var n = RED.nodes.node(this.links[i]);
            if (n && n.links.indexOf(this.id) === -1) {
                n.links.push(this.id);
            }
        }
    }

    RED.nodes.registerType('link in',{
        category: 'common',
        color:"#ddd",//"#87D8CF",
        defaults: {
            name: { value: "_DEFAULT_" },
            links: { value: [], type:"link out[]" }
        },
        inputs:0,
        outputs:1,
        icon: "link-out.svg",
        outputLabels: function(i) {
            return this.name||this._("link.linkIn");
        },
        showLabel: false,
        label: function() {
            return this.name||this._("link.linkIn");
        },
        labelStyle: function() {
            return this.name?"node_label_italic":"";
        },
        oneditprepare: function() {
            onEditPrepare(this,"link out");
        },
        oneditsave: function() {
            const result = onEditSave(this);
            // In case the name has changed, ensure any link call nodes on this
            // tab are redrawn with the updated name
            var localCallNodes = RED.nodes.filterNodes({z:RED.workspaces.active(), type:"link call"});
            localCallNodes.forEach(function(node) {
                node.dirty = true;
            });
            return result
        },
        onadd: onAdd,
        oneditresize: resizeNodeList
    });

    RED.nodes.registerType('link call',{
        category: 'common',
        color:"#ddd",//"#87D8CF",
        defaults: {
            name: { value: "" },
            links: {
                value: [],
                type: "link in[]",
                validate: function (v, opt) {
                    if (((this.linkType || "static") === "static" && v.length > 0)
                      || this.linkType === "dynamic") {
                        return true;
                    }
                    return RED._("node-red:link.errors.linkUndefined");
                }
            },
            linkType: { value:"static" },
            timeout: {
                value: "30",
                label: RED._("node-red:link.timeout"),
                validate:RED.validators.number(true)
            }
        },
        inputs: 1,
        outputs: 1,
        icon: "link-call.svg",
        inputLabels: function(i) {
            return this.name||this._("link.linkCall");
        },
        label: function() {
            if (this.name) {
                return this.name;
            }
            if (this.linkType === "dynamic") {
                return this._("link.dynamicLinkLabel");
            } else if (this.links.length > 0) {
                var targetNode = RED.nodes.node(this.links[0]);
                return targetNode && (targetNode.name || this._("link.linkCall"));
            }
            return this._("inject.none");
        },
        labelStyle: function() {
            return this.name?"node_label_italic":"";
        },
        oneditprepare: function() {
            const updateVisibility = function() {
                const static = $('#node-input-linkType').val() !== "dynamic";
                if(static) {
                    $("div.link-call-target-tree").show();
                } else {
                    $("div.link-call-target-tree").hide();
                }
            }
            $("#node-input-linkType").on("change",function(d){
                updateVisibility();
            });
            if (["static","dynamic"].indexOf(this.linkType) < 0) {
                $("#node-input-linkType").val('static');
            }
            updateVisibility();
            onEditPrepare(this,"link in");
        },
        oneditsave: function() {
            return onEditSave(this);
        },
        oneditresize: resizeNodeList
    });

    RED.nodes.registerType('link out',{
        category: 'common',
        color:"#ddd",//"#87D8CF",
        defaults: {
            name: { value:"_DEFAULT_" },
            mode: { value: "link" },// link || return
            links: { value: [], type:"link in[]" }
        },
        align:"right",
        inputs:1,
        outputs:0,
        icon: function() {
            if (this.mode === "return") {
                return "link-return.svg";
            } else {
                return "link-out.svg";
            }
        },
        inputLabels: function(i) {
            return this.name||(this.mode === "return" ?this._("link.linkOutReturn"):this._("link.linkOut"));
        },
        showLabel: false,
        label: function() {
            return this.name||(this.mode === "return" ?this._("link.linkOutReturn"):this._("link.linkOut"));
        },
        labelStyle: function() {
            return this.name?"node_label_italic":"";
        },
        oneditprepare: function() {
            onEditPrepare(this,"link in");
            $("#node-input-mode").on("change", function() {
                $(".node-input-link-rows").toggle(this.value === "link")
            })
            if (!this.mode) {
                $("#node-input-mode").val('link').trigger("change");
            }

        },
        oneditsave: function() {
            return onEditSave(this);
        },
        onadd: onAdd,
        oneditresize: resizeNodeList
    });



})();
</script>
